﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

//public interface IQEvaluable
//{
//    string Evalute();
//}
public class QCriteriaBase
{
    public eQLogicOperator? LogicalOp { get; set; }
    public IList<IQEvaluable> InnerCriterias { get; set; }
    public QCriteriaBase()
    {
        this.InnerCriterias = new List<IQEvaluable>();
    }
}
public class QCriteria : QCriteriaBase, IQEvaluable
{
    public eQComparison Op { get; set; }
    public IQEvaluable Lhs { get; set; }
    public IQEvaluable Rhs { get; set; }
    public QCriteria(string lhs, string rhs) : this(lhs: new QColumn(lhs), op: eQComparison.Eq, rhs: new QColumn(rhs)) { }
    public QCriteria(string lhs, IQEvaluable rhs) : this(lhs: new QColumn(lhs), op: eQComparison.Eq, rhs: rhs) { }
    public QCriteria(IQEvaluable lhs, IQEvaluable rhs) : this(lhs: lhs, op: eQComparison.Eq, rhs: rhs) { }
    public QCriteria(string lhs, object rhs) : this(lhs: new QColumn(lhs), op: eQComparison.Eq, rhs: new QConstant(rhs)) { }
    public QCriteria(IQEvaluable lhs, eQComparison op, IQEvaluable rhs)
        : this(logicalOp: null, lhs: lhs, op: op, rhs: rhs, innerCriterias: new List<IQEvaluable>() { })
    {        
    }
    public QCriteria(IQEvaluable lhs, eQComparison op, Object rhs)
        : this(logicalOp: null, lhs: lhs, op: op, rhs: new QConstant(rhs), innerCriterias: new List<IQEvaluable>() { })
    {
    }
    public QCriteria(string lhs, eQComparison op, Object rhs)
        : this(logicalOp: null, lhs: new QColumn(lhs), op: op, rhs: new QConstant(rhs), innerCriterias: new List<IQEvaluable>() { })
    {
    }
    public QCriteria(string lhs, eQComparison op, string rhs)
        : this(logicalOp: null, lhs: new QColumn(lhs), op: op, rhs: new QColumn(rhs), innerCriterias: new List<IQEvaluable>() { })
    {
    }
    public QCriteria(string lhs, eQComparison op, IQEvaluable rhs)
        : this(logicalOp: null, lhs: new QColumn(lhs), op: op, rhs: rhs, innerCriterias: new List<IQEvaluable>() { })
    {
    }
    public QCriteria(IQEvaluable lhs, eQComparison op, IQEvaluable rhs, IQEvaluable innerCriteria)
        : this(logicalOp: null, lhs: lhs, op: op, rhs: rhs, innerCriterias: new List<IQEvaluable>() { innerCriteria })
    {
    }

    public QCriteria(IQEvaluable lhs, eQComparison op, IQEvaluable rhs, IList<IQEvaluable> innerCriterias)
        : this(logicalOp: null, lhs: lhs, op: op, rhs: rhs, innerCriterias: innerCriterias)
    {
    }
    public QCriteria(eQLogicOperator? logicalOp, IQEvaluable lhs, eQComparison op, IQEvaluable rhs)
        : this(logicalOp: logicalOp, lhs: lhs, op: op, rhs: rhs, innerCriterias: new List<IQEvaluable>() { })
    {
    }
    public QCriteria(eQLogicOperator? logicalOp, IQEvaluable lhs, eQComparison op, IQEvaluable rhs, IQEvaluable innerCriteria)
        : this(logicalOp: logicalOp, lhs: lhs, op: op, rhs: rhs, innerCriterias: new List<IQEvaluable>() { innerCriteria })
    {
    }
    public QCriteria(eQLogicOperator? logicalOp, IQEvaluable lhs, eQComparison op, IQEvaluable rhs, IList<IQEvaluable> innerCriterias)
    {
        this.Lhs = lhs;
        this.Op = op;
        this.Rhs = rhs;
        this.LogicalOp = logicalOp;
        this.InnerCriterias = innerCriterias;
    }
    public string Evalute()
    {
        //return string.Format("{0} {1} {2}", this.Lhs.Evalute(),
        //                          Comparision.ToString(this.Op),
        //                          this.Rhs.Evalute());

        StringBuilder sb = new StringBuilder();

        if (this.LogicalOp != null)
        {
            sb.AppendFormat(LogicOperator.ToString(this.LogicalOp.Value));
        }
        sb.Append("(");
        //sb.AppendFormat("({0})", this.Criteria.Evalute());
        if(this.Op == eQComparison.In)
            sb.AppendFormat("({0} {1} ({2}))", this.Lhs.Evalute(),
                                  Comparision.ToString(this.Op),
                                  this.Rhs.Evalute());

        else 
        sb.AppendFormat("({0} {1} {2})", this.Lhs.Evalute(),
                                  Comparision.ToString(this.Op),
                                  this.Rhs.Evalute());

        if (this.InnerCriterias.Count > 0)
        {
            for (int j = 0; j < this.InnerCriterias.Count; j++)
            {
                IQEvaluable criteria = this.InnerCriterias[j];
                sb.Append(criteria.Evalute());
            }
        }
        sb.Append(")");
        return sb.ToString();
    }


    public string EvaluteWithAlias()
    {
        throw new NotImplementedException();
    }
}


public class QExists : QCriteriaBase, IQEvaluable
{
    public QueryFacade Query { get; set; }

    public QExists(QueryFacade query)
        : this(logicalOp: null, query: query, innerCriterias: new List<IQEvaluable>() { })
    {
    }

    public QExists(eQLogicOperator? logicalOp, QueryFacade query)
        : this(logicalOp: logicalOp, query: query, innerCriterias: new List<IQEvaluable>() { })
    {
    }
    public QExists(eQLogicOperator? logicalOp, QueryFacade query, IList<IQEvaluable> innerCriterias)
    {
        this.Query = query;
        this.LogicalOp = logicalOp;
        this.InnerCriterias = innerCriterias;
    }

    public string Evalute()
    {
        // return string.Format(" exists ({0})", this.Query.ToString());
        StringBuilder sb = new StringBuilder();

        if (this.LogicalOp != null)
        {
            sb.AppendFormat(LogicOperator.ToString(this.LogicalOp.Value));
        }
        sb.Append("(");
        //sb.AppendFormat("({0})", this.Criteria.Evalute());
        sb.AppendFormat("(exists ({0}))", this.Query.ToString());

        if (this.InnerCriterias.Count > 0)
        {
            for (int j = 0; j < this.InnerCriterias.Count; j++)
            {
                IQEvaluable criteria = this.InnerCriterias[j];
                sb.Append(criteria.Evalute());
            }
        }
        sb.Append(")");
        return sb.ToString();
    }


    public string EvaluteWithAlias()
    {
        throw new NotImplementedException();
    }
}

public class QCriteriaBetween : QCriteriaBase, IQEvaluable
{
    public IQEvaluable Operand { get; set; }
    public IQEvaluable Operand1 { get; set; }
    public IQEvaluable Operand2 { get; set; }

    public QCriteriaBetween(string operand, string operand1, string operand2)
        : this(logicalOp: null, operand: new QColumn(operand), operand1: new QColumn(operand1), operand2: new QColumn(operand2), innerCriterias: new List<IQEvaluable>())
    {
    }
    public QCriteriaBetween(IQEvaluable operand, IQEvaluable operand1, IQEvaluable operand2)
        : this(logicalOp: null, operand: operand, operand1: operand1, operand2: operand2, innerCriterias: new List<IQEvaluable>())
    {
    }
    public QCriteriaBetween(eQLogicOperator? logicalOp, IQEvaluable operand, IQEvaluable operand1, IQEvaluable operand2)
        : this(logicalOp: null, operand: operand, operand1: operand1, operand2: operand2, innerCriterias: new List<IQEvaluable>())
    {

    }
    public QCriteriaBetween(eQLogicOperator? logicalOp, IQEvaluable operand, IQEvaluable operand1, IQEvaluable operand2, IList<IQEvaluable> innerCriterias)
    {
        this.Operand = operand;
        this.Operand1 = operand1;
        this.Operand2 = operand2;
        this.LogicalOp = logicalOp;
        this.InnerCriterias = innerCriterias;
    }

    public string Evalute()
    {
        //return string.Format(" {0} between  {1} and {2}", this.Operand.Evalute(), this.Operand1.Evalute(), this.Operand2.Evalute());
        StringBuilder sb = new StringBuilder();

        if (this.LogicalOp != null)
        {
            sb.AppendFormat(LogicOperator.ToString(this.LogicalOp.Value));
        }
        sb.Append("(");
        //sb.AppendFormat("({0})", this.Criteria.Evalute());
        sb.AppendFormat("({0} between  {1} and {2})", this.Operand.Evalute(), this.Operand1.Evalute(), this.Operand2.Evalute());

        if (this.InnerCriterias.Count > 0)
        {
            for (int j = 0; j < this.InnerCriterias.Count; j++)
            {
                IQEvaluable criteria = this.InnerCriterias[j];
                sb.Append(criteria.Evalute());
            }
        }
        sb.Append(")");
        return sb.ToString();

    }


    public string EvaluteWithAlias()
    {
        throw new NotImplementedException();
    }
}
